package com.dolphin.common.expr;

import com.dolphin.common.component.var.enums.ValueTypeEnum;
import com.dolphin.common.component.var.enums.VarTypeEnum;
import com.dolphin.common.exception.EmusException;
import com.dolphin.common.expr.emus.ConditionNodeTypeEnum;
import com.dolphin.common.expr.emus.OperateTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author shida
 */
public class AviatorExpressionGenerator {
    private static final String SPLIT_SYMBOL = "[0]";
    private static final Pattern pattern = Pattern.compile("\\[\\d+]");
    private static final int TWO = 2;
    private static final int NINE = 9;

    public static String generateAviatorScript(Condition condition, Map<String,String> varIdAndFieldExpMap){
        StringBuilder exp = new StringBuilder();

        if(condition.getNodeType().isItem() == true){
            String leftParam = getSourceFieldExpr(varIdAndFieldExpMap, condition.getVarBid());
            OperateTypeEnum operateType = condition.getOperateType();
            switch (operateType){
                case EQ:
                case NE:
                case GT:
                case LT:
                case GQ:
                case LQ:{
                    exp.append(leftParam).append(operateType.getCode());
                    convertAndAppendValue(exp, condition, varIdAndFieldExpMap);
                    break;
                }
                case IN:
                case NOTIN:
                case NOTLIKE:
                case LK:{
                    exp.append(operateType.getCode()).append("(").append(leftParam).append(",");
                    convertAndAppendValue(exp, condition, varIdAndFieldExpMap);
                    exp.append(")");
                    break;
                }
                case NN:{
                    exp.append(leftParam).append(OperateTypeEnum.NE.getCode()).append("nil");
                    exp.append("notBlank(").append(leftParam).append(")");
                    break;
                }
                case NV:{
                    exp.append(leftParam).append(OperateTypeEnum.EQ.getCode()).append("nil");
                    exp.append("!notBlank(").append(leftParam).append(")");
                    break;
                }
                case HASVAL:{
                    exp.append(leftParam).append(OperateTypeEnum.NE.getCode()).append("''");
                    break;
                }
                case NOHASVAL:{
                    exp.append(leftParam).append(OperateTypeEnum.EQ.getCode()).append("''");
                    break;
                }
                case BW:{
                    String[] values = condition.getValue().split(",");
                    if(values.length >= 1){
                        exp.append(leftParam).append(OperateTypeEnum.GQ.getCode())
                                .append(toValue(condition.getValueType(), values[0]));
                    }
                    if(values.length >= TWO){
                        exp.append(" && ").append(leftParam).append(OperateTypeEnum.LT.getCode())
                                .append(toValue(condition.getValueType(), values[1]));
                    }
                    break;
                }
                default:
                    break;
            }
        } else if (CollectionUtils.isEmpty(condition.getChildren()) == false) {
            for (int i=0;i<condition.getChildren().size();i++){
                exp.append("(").append(generateAviatorScript(condition.getChildren().get(i), varIdAndFieldExpMap))
                        .append(")");
                if(i < condition.getChildren().size() - 1){
                    exp.append(" ").append(condition.getRelation().getFlag()).append(" ");
                }
            }
        }

        return exp.toString();
    }

    public static String genExpression(Condition condition){
        StringBuilder exp = new StringBuilder();
        if(condition.getNodeType() == ConditionNodeTypeEnum.ITEM){
            String f = condition.getVarBid();
            OperateTypeEnum operateType = condition.getOperateType();

            switch (operateType){
                case EQ:
                case NE:
                case GT:
                case LT:
                case GQ:
                case LQ:{
                    exp.append(f).append(operateType.getCode());
                    if(StringUtils.isNotEmpty(condition.getVarBidValue()) == true){
                        //举例：(c5 == s62)
                        exp.append(condition.getVarBidValue());
                    }else {
                        if(condition.isFlag() == false && isBid(condition.getValue()) == true){
                            String value = StringUtils.isEmpty(condition.getValue()) ? "''" : condition.getValue();
                            exp.append(value);
                        }else {
                            //举例：(a5 > 10)
                            exp.append(toValue(condition.getValueType(), condition.getValue()));
                        }
                    }
                    break;
                }
                case IN:
                case NOTIN:
                case NOTLIKE:
                case LK:{
                    exp.append(operateType.getCode()).append("(").append(f).append(",");
                    if(StringUtils.isEmpty(condition.getVarBidValue()) == false){
                        exp.append(condition.getVarBidValue());
                    }else {
                        if(condition.isFlag() == false && isBid(condition.getValue()) == true){
                            String value = StringUtils.isEmpty(condition.getValue()) ? "''" : condition.getValue();
                            exp.append(value);
                        }else {
                            exp.append(toValue(condition.getValueType(), condition.getValue()));
                        }
                    }

                    exp.append(")");
                    break;
                }
                case NN:{
                    exp.append(f).append(OperateTypeEnum.NE.getCode()).append("nil");
                    exp.append("notBlank(").append(f).append(")");
                    break;
                }
                case NV:{
                    exp.append(f).append(OperateTypeEnum.EQ.getCode()).append("nil");
                    exp.append("!notBlank(").append(f).append(")");
                    break;
                }
                case HASVAL:{
                    exp.append(f).append(OperateTypeEnum.NE.getCode()).append("''");
                    break;
                }
                case NOHASVAL:{
                    exp.append(f).append(OperateTypeEnum.EQ.getCode()).append("''");
                    break;
                }
                case BW:{
                    String[] values = condition.getValue().split(",");
                    if(values.length >= 1){
                        exp.append(f).append(OperateTypeEnum.GQ.getCode())
                                .append(toValue(condition.getValueType(), values[0]));
                    }
                    if(values.length >= TWO){
                        exp.append(" && ").append(f).append(OperateTypeEnum.LT.getCode())
                                .append(toValue(condition.getValueType(), values[1]));
                    }
                    break;
                }
                default:
                    break;
            }
        }else if (CollectionUtils.isEmpty(condition.getChildren()) == false) {
            for (int i=0;i<condition.getChildren().size();i++){
                exp.append("(").append(genExpression(condition.getChildren().get(i)))
                        .append(")");
                if(i < condition.getChildren().size() - 1){
                    exp.append(" ").append(condition.getRelation().getFlag()).append(" ");
                }
            }
        }

        return exp.toString();
    }

    private static void convertAndAppendValue(StringBuilder exp, Condition condition, Map<String,String> varIdAndFieldExpMap){
        if(StringUtils.isNotEmpty(condition.getVarBidValue()) == true){
            exp.append(getSourceFieldExpr(varIdAndFieldExpMap, condition.getVarBid()));
        }else {
            if(condition.isFlag() == false && isBid(condition.getValue()) == true){
                exp.append(StringUtils.isEmpty(condition.getValue()) ? "" : getSourceFieldExpr(varIdAndFieldExpMap, condition.getValue()));
            }else {
                exp.append(toValue(condition.getValueType(), condition.getValue()));
            }
        }
    }

    public static boolean isBid(String value){
        if(StringUtils.isEmpty(value) || value.length() != NINE){
            return false;
        }

        String tag = value.substring(0, 1);
        if(VarTypeEnum.isBid(tag) == false){
            return false;
        }

        String id = value.substring(1);
        return NumberUtils.isDigits(id);
    }

    private static Object toValue(ValueTypeEnum valueType, String value){
        switch (valueType){
            case STRING:
            case VAR:
            case DICTIONARY:
            case MAP:
            case DATE:
            case LIST:
            {
                return StringUtils.isEmpty(value) ? "''" : "'" + value + "'";
            }
            case ACCOUNT:
            case AMT:
            case NUM:
            case INTEGER:
            case TIMESTAMP:
            case LONG:
            case FLOAT:
            case DOUBLE:
            case INT:
            {
                return StringUtils.isEmpty(value) ? 0 : value;
            }
            case BOOLEAN:
            default:{
                return value;
            }
        }
    }

    private static String getSourceFieldExpr(Map<String, String> fieldExpMap, String bid){
        String dynamicParam = fieldExpMap.get(bid.substring(1));
        if(StringUtils.isEmpty(dynamicParam) == true){
            throw new EmusException("找不到bid=" + bid + ", 对应的样本映射字段");
        }

        if(dynamicParam.startsWith("$.") == true){
            dynamicParam = dynamicParam.replaceAll("\\$.", "");
        }

        Matcher matcher = pattern.matcher(dynamicParam);
        if(matcher.find()){
            return "#" + dynamicParam.replaceAll("\\[", ".[");
        }

        return dynamicParam;
    }
}
